#include <stdio.h>
#include <string.h>
#include "relic.h"
#include "sqft_conf.h"
#include <math.h>
#include <sys/time.h>

/**--------------------------------------------------------------------*/
/**
 * Funcao para calculo de intervalo de tempo de execuao
 * Precisao: microsegundos (us)
 */
 
long long unsigned getTimeDifff(struct timeval start, struct timeval stop) {
	long long unsigned time;
	time = (float) (stop.tv_sec - start.tv_sec)*1000000;
	time += ((float)(stop.tv_usec - start.tv_usec));
	return time;
}


/**--------------------------------------------------------------------*/

int main(int argc, char *argv[]) {
	int MAX, is_prime;
	long long unsigned acc_sqft1, acc_sqft2, acc_sqft3, acc_sqft4, acc_sqft5;
	struct timeval start, stop;
	FILE *fp_sqft1, *fp_sqft2, *fp_sqft3, *fp_sqft4, *fp_sqft5;
	int bits, i, j;
	//int sts[4];
	bn_t p;
	
	if (core_init() != STS_OK) {
		core_clean();
		return 1;
	}
	
	fp_sqft1 = fopen("sts/sqft1.dat", "w");
	fp_sqft2 = fopen("sts/sqft2.dat", "w");
	fp_sqft3 = fopen("sts/sqft3.dat", "w");
	fp_sqft4 = fopen("sts/sqft4.dat", "w");
	fp_sqft5 = fopen("sts/sqft5.dat", "w");
	fprintf(fp_sqft1, "# SQFT v1 \n");
	fprintf(fp_sqft2, "# SQFT v2 \n");	
	fprintf(fp_sqft3, "# SQFT v3 \n");	
	fprintf(fp_sqft4, "# SQFT v4 \n");	
	fprintf(fp_sqft5, "# SQFT v5 \n");
	bn_null(p);
	bn_new(p);
	
	if (argc > 1) {
		bits = atoi(argv[1]);
		
		for (i=0; i<20; i++) {
			bn_gen_prime(p, bits);
			
			gettimeofday(&start, NULL);
			bn_is_prime_sqft_v1(p);
			gettimeofday(&stop, NULL);
			fprintf(fp_sqft1, "%d\t%llu\n", i, getTimeDifff(start, stop));
			
			gettimeofday(&start, NULL);
			bn_is_prime_sqft_v2(p);
			gettimeofday(&stop, NULL);
			fprintf(fp_sqft2, "%d\t%llu\n", i, getTimeDifff(start, stop));
			
			gettimeofday(&start, NULL);
			bn_is_prime_sqft_v3(p);
			gettimeofday(&stop, NULL);
			fprintf(fp_sqft3, "%d\t%llu\n", i, getTimeDifff(start, stop));
			
			gettimeofday(&start, NULL);
			bn_is_prime_sqft_v4(p);
			gettimeofday(&stop, NULL);
			fprintf(fp_sqft4, "%d\t%llu\n", i, getTimeDifff(start, stop));
			
			gettimeofday(&start, NULL);
			bn_is_prime_sqft_v5(p);
			gettimeofday(&stop, NULL);
			fprintf(fp_sqft5, "%d\t%llu\n", i, getTimeDifff(start, stop));
		}
		
	}
	else {
		MAX = 20;
		for (bits=8; bits<=BN_BITS; bits *= 2) {
			acc_sqft1=0;
			acc_sqft2=0;
			acc_sqft3=0;
			acc_sqft4=0;
			acc_sqft5=0;
			printf("Getting times for %d BITS\n", bits);
			
			for (j=0; j<MAX; j++) {
				is_prime=0;
				while (!is_prime) {
					do {
						bn_rand(p, BN_POS, bits);
					} while (bn_bits(p) != bits);
					//bn_gen_prime(p, bits);
					
					gettimeofday(&start, NULL);
					if (bn_is_prime_basic(p)) {
						if (bn_is_prime_sqft_v1(p)) {
							is_prime=1;
						}
					}
					gettimeofday(&stop, NULL);
				
					acc_sqft1 += getTimeDifff(start, stop);
	
					gettimeofday(&start, NULL);
					if (bn_is_prime_basic(p)) {
						if (bn_is_prime_sqft_v2(p)) {
							if (!is_prime) {
								printf("\nWTF - ERROR\n");
								return 1;
							}
						}
					}
					gettimeofday(&stop, NULL);
				
					acc_sqft2 += getTimeDifff(start, stop);
					
					gettimeofday(&start, NULL);
					if (bn_is_prime_basic(p)) {
						if (bn_is_prime_sqft_v3(p)) {
							if (!is_prime) {
								printf("\nWTF - ERROR\n");
								return 1;
							}
						}
					}
					gettimeofday(&stop, NULL);
				
					acc_sqft3 += getTimeDifff(start, stop);
					
					gettimeofday(&start, NULL);
					if (bn_is_prime_basic(p)) {
						if (bn_is_prime_sqft_v4(p)) {
							if (!is_prime) {
								printf("\nWTF - ERROR\n");
								return 1;
							}
						}
					}
					gettimeofday(&stop, NULL);
				
					acc_sqft4 += getTimeDifff(start, stop);
					
					gettimeofday(&start, NULL);
					if (bn_is_prime_basic(p)) {
						if (bn_is_prime_sqft_v5(p)) {
							if (!is_prime) {
								printf("\nWTF - ERROR\n");
								return 1;
							}
						}
					}
					gettimeofday(&stop, NULL);
				
					acc_sqft5 += getTimeDifff(start, stop);
				}
			}
			fprintf(fp_sqft1, "%d %llu\n", (int) log2((double) bits), acc_sqft1/MAX);
			fprintf(fp_sqft2, "%d %llu\n", (int) log2((double) bits), acc_sqft2/MAX);
			fprintf(fp_sqft3, "%d %llu\n", (int) log2((double) bits), acc_sqft3/MAX);
			fprintf(fp_sqft4, "%d %llu\n", (int) log2((double) bits), acc_sqft4/MAX);
			fprintf(fp_sqft5, "%d %llu\n", (int) log2((double) bits), acc_sqft5/MAX);
		}
		
	}

	
	
	bn_free(p);
	fclose(fp_sqft1);
	fclose(fp_sqft2);
	fclose(fp_sqft3);
	fclose(fp_sqft4);
	core_clean();
	return 0;
}
